package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type Request struct {
	Model    string    `json:"model"`
	Messages []Message `json:"messages"`
}

type Response struct {
	Choices []struct {
		Message struct {
			Content string `json:"content"`
		} `json:"message"`
	} `json:"choices"`
}

type Config struct {
	AI struct {
		BaseURL string `json:"base_url"`
		Model   string `json:"model"`
		APIKey  string `json:"api_key"`
	} `json:"ai"`
	Server struct {
		Port int `json:"port"`
	} `json:"server"`
	SavePath string `json:"save_path"`
	Log      struct {
		Path    string `json:"path"`
		MaxDays int    `json:"max_days"`
		MaxSize int    `json:"max_size"`
	} `json:"log"`
	Version string `json:"version"`
}

type Review struct {
	Path string
	Date string
	Time string
}

type MonthGroup struct {
	Title   string
	Reviews []Review
}

var config Config
var logger *zap.Logger

func loadConfig() error {
	// 首先尝试读取当前目录的配置文件
	configFile := "config.json"
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		// 如果配置文件不存在，创建默认配置
		defaultConfig := Config{
			AI: struct {
				BaseURL string `json:"base_url"`
				Model   string `json:"model"`
				APIKey  string `json:"api_key"`
			}{
				BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
				Model:   "qwen-plus",
				APIKey:  "sk-28e5a3bbcfe044d1a799b9571fe35b13",
			},
			SavePath: "reviews",
		}

		// 将默认配置写入文件
		jsonData, err := json.MarshalIndent(defaultConfig, "", "    ")
		if err != nil {
			return fmt.Errorf("生成默认配置失败: %v", err)
		}

		if err := ioutil.WriteFile(configFile, jsonData, 0644); err != nil {
			return fmt.Errorf("写入默认配置失败: %v", err)
		}

		config = defaultConfig
		return nil
	}

	// 读取配置文件
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		return fmt.Errorf("读取配置文件失败: %v", err)
	}

	// 解析配置
	if err := json.Unmarshal(data, &config); err != nil {
		return fmt.Errorf("解析配置文件失败: %v", err)
	}

	// 设置默认端口
	if config.Server.Port == 0 {
		config.Server.Port = 8060
	}

	return nil
}

func getQuestions() ([]string, error) {
	prompt := `作为一个专业的复盘引导师，请生成5个引导性问题来帮助用户进行今天的复盘。要求：
1. 问题要具体且有启发性
2. 问题要覆盖：今日成果、遇到的挑战、收获感悟、改进空间、未来计划
3. 每个问题都要简洁明了
4. 问题要有序号
请直接输出5个问题，每行一个，前面带序号。`

	response, err := chatWithAI(prompt)
	if err != nil {
		return nil, err
	}

	// 将AI回答按行分割成问题数组
	questions := strings.Split(strings.TrimSpace(response), "\n")
	return questions, nil
}

func chatWithAI(prompt string) (string, error) {
	messages := []Message{
		{Role: "user", Content: prompt},
	}

	requestBody := Request{
		Model:    config.AI.Model,
		Messages: messages,
	}

	jsonData, err := json.Marshal(requestBody)
	if err != nil {
		return "", fmt.Errorf("JSON编码错误: %v", err)
	}

	req, err := http.NewRequest("POST", config.AI.BaseURL+"/chat/completions", bytes.NewBuffer(jsonData))
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+config.AI.APIKey)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("发送请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应失败: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("API请求失败，状态码: %d，响应: %s", resp.StatusCode, string(body))
	}

	var response Response
	if err := json.Unmarshal(body, &response); err != nil {
		return "", fmt.Errorf("解析响应失败: %v", err)
	}

	if len(response.Choices) == 0 {
		return "", fmt.Errorf("AI没有返回答案")
	}

	return response.Choices[0].Message.Content, nil
}

func saveReview(questions []string, answers []string, aiAnalysis string) error {
	now := time.Now()

	// 使用配置的保存路径
	monthDir := fmt.Sprintf("%s/%s/%02d", config.SavePath, now.Format("2006"), now.Month())
	if err := os.MkdirAll(monthDir, 0755); err != nil {
		logger.Error(fmt.Sprintf("创建月份目录失败: %v", err))
		return err
	}

	// 生成包含时分的文件名
	filename := fmt.Sprintf("%s/review_%s.html",
		monthDir,
		now.Format("2006-01-02-15-04"))

	// HTML模板
	htmlTemplate := `
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>每日复盘 - %s</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            line-height: 1.6;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background-color: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        h1 {
            color: #2c3e50;
            text-align: center;
            border-bottom: 2px solid #eee;
            padding-bottom: 10px;
        }
        .date {
            text-align: center;
            color: #666;
            margin-bottom: 30px;
        }
        .section {
            margin-bottom: 25px;
        }
        .section-title {
            color: #2c3e50;
            font-size: 1.2em;
            font-weight: bold;
            margin-bottom: 10px;
        }
        .content {
            background-color: #f9f9f9;
            padding: 15px;
            border-radius: 5px;
            border-left: 4px solid #3498db;
        }
        .ai-analysis {
            margin-top: 40px;
            padding: 20px;
            background-color: #f0f7ff;
            border-radius: 5px;
        }
        .ai-analysis h2 {
            color: #2980b9;
            margin-top: 0;
        }
        .file-info {
            text-align: right;
            color: #666;
            font-size: 0.9em;
            margin-top: 20px;
            padding-top: 20px;
            border-top: 1px solid #eee;
        }
        .breadcrumb {
            color: #666;
            font-size: 0.9em;
            margin-bottom: 20px;
        }
        .breadcrumb a {
            color: #3498db;
            text-decoration: none;
        }
        .breadcrumb a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="breadcrumb">
            位置：%s年 / %s月 / %s
        </div>
        <h1>每日复盘记录</h1>
        <div class="date">%s</div>

        <div class="section">
            <div class="section-title">复盘问答</div>
            %s
        </div>

        <div class="ai-analysis">
            <h2>AI 分析与建议</h2>
            <div class="content">%s</div>
        </div>

        <div class="file-info">
            创建时间：%s
        </div>
    </div>
</body>
</html>
`

	// 格式化问答内容
	var qaContent strings.Builder
	for i, question := range questions {
		qaContent.WriteString(fmt.Sprintf(`
            <div class="content" style="margin-bottom: 15px">
                <div style="color: #2c3e50; margin-bottom: 8px">%s</div>
                <div>%s</div>
            </div>
        `, question, strings.ReplaceAll(answers[i], "\n", "<br>")))
	}

	// 处理换行符
	aiAnalysis = strings.ReplaceAll(aiAnalysis, "\n", "<br>")

	// 格式化HTML内容
	htmlContent := fmt.Sprintf(
		htmlTemplate,
		now.Format("2006-01-02 15:04"),    // 标题
		now.Format("2006"),                // 年份
		now.Format("01"),                  // 月份
		now.Format("2006-01-02 15:04"),    // 文件名
		now.Format("2006年01月02日 15:04"),   // 显示日期
		qaContent.String(),                // 问答内容
		aiAnalysis,                        // AI分析
		now.Format("2006-01-02 15:04:05"), // 创建时间
	)

	// 写入文件
	err := ioutil.WriteFile(filename, []byte(htmlContent), 0644)
	if err != nil {
		logger.Error(fmt.Sprintf("保存复盘记录失败: %v", err))
		return err
	}

	logger.Info(fmt.Sprintf("复盘记录已保存至: %s", filename))
	return nil
}

func getHistoryRecords() ([]MonthGroup, error) {
	var months []MonthGroup
	basePath := config.SavePath

	// 确保基础路径存在
	if err := os.MkdirAll(basePath, 0755); err != nil {
		return nil, fmt.Errorf("创建目录失败: %v", err)
	}

	// 获取所有年份目录
	years, err := ioutil.ReadDir(basePath)
	if err != nil {
		return nil, fmt.Errorf("读取年份目录失败: %v", err)
	}

	// 按年份降序排序
	sort.Slice(years, func(i, j int) bool {
		return years[i].Name() > years[j].Name()
	})

	for _, year := range years {
		if !year.IsDir() {
			continue
		}

		yearPath := filepath.Join(basePath, year.Name())
		monthDirs, err := ioutil.ReadDir(yearPath)
		if err != nil {
			logger.Warn("读取月份目录失败",
				zap.String("path", yearPath),
				zap.Error(err))
			continue
		}

		// 按月份降序排序
		sort.Slice(monthDirs, func(i, j int) bool {
			return monthDirs[i].Name() > monthDirs[j].Name()
		})

		for _, month := range monthDirs {
			if !month.IsDir() {
				continue
			}

			monthPath := filepath.Join(yearPath, month.Name())
			files, err := ioutil.ReadDir(monthPath)
			if err != nil {
				logger.Error(fmt.Sprintf("读取月份文件失败: %v", err))
				continue
			}

			var reviews []Review
			for _, file := range files {
				if !strings.HasSuffix(file.Name(), ".html") {
					continue
				}

				// 解析文件名
				name := strings.TrimSuffix(file.Name(), ".html")
				dateTime := strings.Split(name, "_")
				if len(dateTime) != 2 {
					continue
				}

				// 提取日期和时间
				date := dateTime[1]
				dateParts := strings.Split(date, "-")
				if len(dateParts) < 4 { // 至少需要年月日时
					continue
				}

				// 格式化显示时间
				displayDate := fmt.Sprintf("%s月%s日", dateParts[1], dateParts[2])
				displayTime := fmt.Sprintf("%s:%s", dateParts[3], dateParts[4])

				reviews = append(reviews, Review{
					Path: fmt.Sprintf("%s/%s/%s",
						year.Name(),
						month.Name(),
						file.Name()),
					Date: displayDate,
					Time: displayTime,
				})
			}

			if len(reviews) > 0 {
				// 按时间降序排序
				sort.Slice(reviews, func(i, j int) bool {
					return reviews[i].Path > reviews[j].Path
				})

				months = append(months, MonthGroup{
					Title:   fmt.Sprintf("%s年%s月", year.Name(), month.Name()),
					Reviews: reviews,
				})
			}
		}
	}
	return months, nil
}

func initLogger() error {
	// 确保日志目录存在
	if err := os.MkdirAll(config.Log.Path, 0755); err != nil {
		return fmt.Errorf("创建日志目录失败: %v", err)
	}

	// 配置日志轮转
	lumberJackLogger := &lumberjack.Logger{
		Filename:   filepath.Join(config.Log.Path, "app.log"),
		MaxSize:    config.Log.MaxSize, // MB
		MaxBackups: 3,
		MaxAge:     config.Log.MaxDays, // days
		Compress:   true,
	}

	// 配置日志级别
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(encoderConfig),
		zapcore.AddSync(lumberJackLogger),
		zap.InfoLevel,
	)

	logger = zap.New(core, zap.AddCaller())
	return nil
}

func cleanupLogs() {
	ticker := time.NewTicker(24 * time.Hour)
	defer ticker.Stop()

	for range ticker.C {
		files, err := ioutil.ReadDir(config.Log.Path)
		if err != nil {
			logger.Error("读取日志目录失败", zap.Error(err))
			continue
		}

		now := time.Now()
		for _, file := range files {
			if file.IsDir() {
				continue
			}

			if now.Sub(file.ModTime()) > time.Duration(config.Log.MaxDays)*24*time.Hour {
				filePath := filepath.Join(config.Log.Path, file.Name())
				if err := os.Remove(filePath); err != nil {
					logger.Error("删除旧日志失败",
						zap.String("file", file.Name()),
						zap.Error(err))
				} else {
					logger.Info("成功删除旧日志",
						zap.String("file", file.Name()))
				}
			}
		}
	}
}

func main() {
	// 加载配置
	if err := loadConfig(); err != nil {
		panic(err)
	}

	// 初始化日志系统
	if err := initLogger(); err != nil {
		panic(err)
	}
	defer logger.Sync()

	// 启动日志清理任务
	go cleanupLogs()

	r := gin.Default()

	// 加载HTML模板
	r.LoadHTMLGlob("templates/*")
	// 提供静态文件服务
	r.Static("/static", "./static")

	// 路由设置
	r.GET("/", func(c *gin.Context) {
		months, err := getHistoryRecords()
		if err != nil {
			c.HTML(http.StatusInternalServerError, "error.html", gin.H{
				"error": err.Error(),
			})
			return
		}

		c.HTML(http.StatusOK, "index.html", gin.H{
			"title":   "复盘助手",
			"version": config.Version,
			"months":  months,
		})
	})

	r.GET("/review", func(c *gin.Context) {
		// 获取AI生成的问题
		questions, err := getQuestions()
		if err != nil {
			c.HTML(http.StatusInternalServerError, "error.html", gin.H{
				"error": err.Error(),
			})
			return
		}

		c.HTML(http.StatusOK, "review.html", gin.H{
			"title":     "今日复盘",
			"date":      time.Now().Format("2006-01-02"),
			"questions": questions,
		})
	})

	r.POST("/review", func(c *gin.Context) {
		var data struct {
			Questions []string `json:"questions"`
			Answers   []string `json:"answers"`
		}

		if err := c.BindJSON(&data); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		// 生成AI分析
		prompt := formatAnswers(data.Questions, data.Answers)
		analysis, err := chatWithAI(prompt)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		// 使用 _ 忽略返回值
		err = saveReview(data.Questions, data.Answers, analysis)
		if err != nil {
			logger.Error("保存复盘记录失败",
				zap.Error(err))
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		logger.Info("成功保存复盘记录",
			zap.Time("time", time.Now()))

		c.JSON(http.StatusOK, gin.H{
			"message":  "复盘已保存",
			"analysis": analysis,
		})
	})

	// 修改查看历史记录的路由
	r.GET("/review/:year/:month/:file", func(c *gin.Context) {
		filePath := filepath.Join(
			config.SavePath,
			c.Param("year"),
			c.Param("month"),
			c.Param("file"),
		)

		if _, err := os.Stat(filePath); os.IsNotExist(err) {
			logger.Warn("找不到复盘记录",
				zap.String("path", filePath))
			c.HTML(http.StatusNotFound, "error.html", gin.H{
				"error": "找不到该复盘记录",
			})
			return
		}

		content, err := ioutil.ReadFile(filePath)
		if err != nil {
			logger.Error("读取复盘记录失败",
				zap.String("path", filePath),
				zap.Error(err))
			c.HTML(http.StatusInternalServerError, "error.html", gin.H{
				"error": "读取复盘记录失败: " + err.Error(),
			})
			return
		}

		logger.Info("成功读取复盘记录",
			zap.String("path", filePath))
		c.Header("Content-Type", "text/html; charset=utf-8")
		c.String(http.StatusOK, string(content))
	})

	// 启动服务器
	addr := fmt.Sprintf(":%d", config.Server.Port)
	logger.Info("启动服务器", zap.String("address", addr))
	if err := r.Run(addr); err != nil {
		logger.Fatal("服务器启动失败", zap.Error(err))
	}
}

func formatAnswers(questions []string, answers []string) string {
	prompt := `作为一个专业的复盘助手，请帮我分析今天的复盘内容并给出建议：

今日复盘内容：
`
	for i, q := range questions {
		prompt += q + "\n答：" + answers[i] + "\n\n"
	}

	prompt += `请从以下几个方面进行分析和建议：
1. 对已完成事项的评价
2. 对遇到挑战的解决建议
3. 对未来计划的优化建议
4. 个人成长的建议

请给出具体、可执行的建议。`

	return prompt
}
